<!--
 * 步进器
 * @作者: 陈老师儿~
 * @手机: 18560000860
 -->
<template>
    <view class="pure-stepper" :class="classes" :style="styles">
        <!-- 减 -->
        <view class="pure-stepper-button pure-stepper-button-minus" v-if="showMinus" @tap="handleMinus" :class="minusClasses">
            <slot name="minus" v-if="$slots.minus"></slot>
            <pure-icon :name="minusIcon" v-else></pure-icon>
        </view>

        <!-- 输入框 -->
        <!-- type 的值动态设置是为了在抖音中做兼容 -->
        <view class="pure-stepper-input" v-if="showInput" :class="inputClasses">
            <pure-input
                v-bind="inputOpts"
                :type="inputType"
                v-model="number"
                :disabled="disabledInput"
                :disabledClass="inputDisabledClass"
                @input="onInput"
                @focus="onFocus"
                @blur="onBlur"
                @confirm="onConfirm"
                @onKeyboardHeightChange="onKeyboardHeightChange"
            ></pure-input>
        </view>

        <!-- 加 -->
        <view class="pure-stepper-button pure-stepper-button-plus" v-if="showPlus" @tap="handlePlus" :class="plusClasses">
            <slot name="plus" v-if="$slots.plus"></slot>
            <pure-icon :name="plusIcon" v-else></pure-icon>
        </view>
    </view>
</template>

<script>
    import props from "./props.js";
    export default {
        options: {
            // 将自定义节点设置成虚拟的
            // 头条、飞书：因为合并后丢失事件和 'provide/inject'
            // 京东：因为合并后不能合并外层 'class'
            // 百度：因为合并后会在控制台疯狂报警告
            // #ifndef MP-TOUTIAO || MP-LARK || MP-JD || MP-BAIDU
            virtualHost: true,
            // #endif

            // 组件样式隔离
            styleIsolation: "apply-shared",

            // 允许使用多个插槽
            multipleSlots: true,
        },
        props: props,
        emits: ["change", "asyncChange", "update:modelValue", "overflowMin", "overflowMax", "overflow", "input", "focus", "blur", "confirm", "keyboardHeightChange"],
        data() {
            return {
                // 值
                number: Number(this.modelValue),
            };
        },
        computed: {
            // classes
            classes() {
                const classes = [];

                // 禁用了整个组件
                if (this.disabled) classes.push(this.disabledClass);

                return classes;
            },

            // styles
            styles() {
                const styles = [];
                return styles;
            },

            // 减号按钮classes
            minusClasses() {
                const classes = [];

                // 禁用
                if (this.isDisabledMinus) classes.push(this.btnDisabledClass || this.disabledClass);

                return classes;
            },

            // 加号按钮classes
            plusClasses() {
                const classes = [];

                // 禁用
                if (this.isDisabledPlus) classes.push(this.btnDisabledClass || this.disabledClass);

                return classes;
            },

            // input输入框的classes
            inputClasses() {
                const classes = [];

                // 禁用
                if (this.disabled || this.disabledInput) classes.push(this.inputDisabledClass || this.disabledClass);

                return classes;
            },

            // 是否禁用减号
            isDisabledMinus() {
                // 禁用了整个组件
                if (this.disabled) return true;
                // 设置了禁用
                if (this.disabledMinus) return true;
                // 当前值已经是最小值了
                if (Number(this.number) <= Number(this.min)) return true;
                return false;
            },

            // 是否禁用加号
            isDisabledPlus() {
                // 禁用了整个组件
                if (this.disabled) return true;
                // 设置了禁用
                if (this.disabledPlus) return true;
                // 当前值已经是最大值了
                if (Number(this.number) >= Number(this.max)) return true;
                return false;
            },

            // 输入框类型
            inputType() {
                // #ifndef MP-TOUTIAO || MP-LARK || MP-XHS
                return "number";
                // #endif

                // #ifdef MP-TOUTIAO || MP-LARK
                return "digit";
                // #endif
            },
        },
        watch: {
            // 监听值
            modelValue: {
                handler(newValue, oldValue) {
                    newValue = Number(newValue);
                    oldValue = Number(oldValue);

                    // 新旧值相同，不处理
                    if (oldValue === newValue) return;

                    // 判断是否超出最小值
                    if (this.isOverflowMin(newValue)) {
                        // 提交溢出事件
                        this.submitOverflowMinEmit(newValue, oldValue);
                        this.submitOverflowEmit(newValue, oldValue);
                        return;
                    }

                    // 判断是否超出最大值
                    if (this.isOverflowMax(newValue)) {
                        // 提交溢出事件
                        this.submitOverflowMaxEmit(newValue, oldValue);
                        this.submitOverflowEmit(newValue, oldValue);
                        return;
                    }

                    // 更新显示的值
                    this.number = newValue;
                },
                immediate: true,
            },

            // 监听值的变化
            number: {
                async handler(newValue, oldValue) {
                    newValue = Number(newValue);
                    oldValue = Number(oldValue);

                    // 新旧值相同，不处理
                    if (oldValue === newValue) return;

                    // 如果默认的值（旧值）本来就超出范围了，如果直接走下面的逻辑，会陷入无限循环
                    // 所以需要处理一下
                    if (this.isOverflowMin(oldValue)) {
                        this.number = Number(this.min);
                        return;
                    }
                    if (this.isOverflowMax(oldValue)) {
                        this.number = Number(this.max);
                        return;
                    }

                    // 判断是否超出最小值
                    if (this.isOverflowMin(newValue)) {
                        // 提交溢出事件
                        this.submitOverflowMinEmit(newValue, oldValue);
                        this.submitOverflowEmit(newValue, oldValue);

                        // 恢复旧值
                        // 需要延迟一下再更新，否则输入框中还会显示新输入的值，不会恢复到旧值
                        await this.sleep();
                        this.number = oldValue;
                        return;
                    }

                    // 判断是否超出最大值
                    if (this.isOverflowMax(newValue)) {
                        // 提交溢出事件
                        this.submitOverflowMaxEmit(newValue, oldValue);
                        this.submitOverflowEmit(newValue, oldValue);

                        // 恢复旧值
                        // 需要延迟一下再更新，否则输入框中还会显示新输入的值，不会恢复到旧值
                        await this.sleep();
                        this.number = oldValue;
                        return;
                    }

                    // 判断是否只能输入整数
                    if (this.onlyInteger) {
                        // 但是输入的不是一个整数
                        if (!this.isInteger(newValue)) {
                            // 最终转成整数
                            await this.sleep();
                            this.number = this.toInteger(newValue);
                        }
                    }

                    // 提交事件
                    this.submitUpdateEmit(newValue, oldValue);
                },
            },
        },
        methods: {
            // 减
            handleMinus() {
                // 被禁用了
                if (this.isDisabledMinus) return;

                // 当前值
                let number = Number(this.number);

                // 步长
                const step = Number(this.step);

                // 当前值 - 步长后的值
                let after = this.accurateOperation(number, step, "-");

                // 异步控制
                if (this.async) {
                    this.submitAsyncChangeEmit(after, number);
                    return;
                }

                // 超出了最小值
                if (this.isOverflowMin(after)) {
                    // 提交溢出事件
                    this.submitOverflowMinEmit(after, number);
                    this.submitOverflowEmit(after, number);

                    return;
                }

                // 更新值
                // 自定义了格式化函数
                if (typeof this.formatter === "function") {
                    this.number = this.formatter(after);
                    return;
                }

                this.number = after;
            },

            // 加
            handlePlus() {
                // 被禁用了
                if (this.isDisabledPlus) return;

                // 当前值
                let number = Number(this.number);

                // 步长
                const step = Number(this.step);

                // 当前值 + 步长后的值
                let after = this.accurateOperation(number, step, "+");

                // 异步控制
                if (this.async) {
                    this.submitAsyncChangeEmit(after, number);
                    return;
                }

                // 超出了最大值
                if (this.isOverflowMax(after)) {
                    // 提交溢出事件
                    this.submitOverflowMaxEmit(after, number);
                    this.submitOverflowEmit(after, number);

                    return;
                }

                // 更新值
                // 自定义了格式化函数
                if (typeof this.formatter === "function") {
                    this.number = this.formatter(after);
                    return;
                }

                this.number = after;
            },

            // 判断是否超出最小值
            isOverflowMin(number) {
                number = Number(number);
                const min = Number(this.min);
                return number < min;
            },

            // 判断是否超出最大值
            isOverflowMax(number) {
                number = Number(number);
                const max = Number(this.max);
                return number > max;
            },

            // 提交更新事件
            submitUpdateEmit(newNumber, oldNumber) {
                this.$emit("update:modelValue", newNumber, oldNumber);
                this.$emit("change", newNumber, oldNumber);
            },

            // 提交异步更新事件
            submitAsyncChangeEmit(newNumber, oldNumber) {
                this.$emit("asyncChange", newNumber, oldNumber);
            },

            // 提交 change 事件
            submitChangeEmit(newNumber, oldNumber) {
                this.$emit("change", newNumber, oldNumber);
            },

            // 提交溢出最小值事件
            submitOverflowMinEmit(newNumber, oldNumber) {
                console.error("绑定值超出最小值：", newNumber, oldNumber);
                this.$emit("overflowMin", newNumber, oldNumber);
            },

            // 提交溢出最大值事件
            submitOverflowMaxEmit(newNumber, oldNumber) {
                console.error("绑定值超出最大值：", newNumber, oldNumber);
                this.$emit("overflowMax", newNumber, oldNumber);
            },

            // 提交溢出事件
            submitOverflowEmit(newNumber, oldNumber) {
                console.error("绑定值超出范围：", newNumber, oldNumber);
                this.$emit("overflow", newNumber, oldNumber);
            },

            // 获取小数点后面的位数
            getFloatDigitsLength(num) {
                const numStr = num.toString();
                const dotIndex = numStr.indexOf(".");
                return dotIndex === -1 ? 0 : numStr.length - dotIndex - 1;
            },

            // 精确运算
            // 提示：两位数的运算还算精确，涉及到3位浮点数的还是不精确
            accurateOperation(a, b, operator) {
                const aDigits = this.getFloatDigitsLength(a);
                const bDigits = this.getFloatDigitsLength(b);
                const maxDigits = Math.max(aDigits, bDigits);
                const multiplier = Math.pow(10, maxDigits);
                const intA = a * multiplier;
                const intB = b * multiplier;

                switch (operator) {
                    case "+":
                        return parseFloat(((intA + intB) / multiplier).toFixed(maxDigits));
                    case "-":
                        return parseFloat(((intA - intB) / multiplier).toFixed(maxDigits));
                    default:
                        throw new Error("Unsupported operator");
                }
            },

            // 转成整数
            toInteger(num) {
                return parseInt(num);
            },

            // 判断一个数字是不是整数，例如：1. 也算整数
            isInteger(num) {
                return num === parseInt(num);
            },

            // 当键盘输入时，触发input事件
            onInput(e) {
                this.$emit("input", e);
            },

            // 输入框聚焦时触发
            onFocus(e) {
                this.$emit("focus", e);
            },

            // 输入框失焦时触发
            onBlur(e) {
                this.$emit("blur", e);
            },

            // 点击完成按钮时触发
            onConfirm(e) {
                this.$emit("confirm", e);
            },

            // 键盘高度发生变化的时候触发此事件
            onKeyboardHeightChange(e) {
                this.$emit("keyboardHeightChange", e);
            },

            // 同步延时
            sleep(time = 50) {
                return new Promise((resolve) => {
                    let timer = setTimeout(() => {
                        clearTimeout(timer);
                        resolve();
                    }, time);
                });
            },
        },
    };
</script>

<style scoped>
    @import "./style.css";
</style>
